import 'dart:async';
import 'dart:io' show HttpStatus;

import 'package:dio/dio.dart';
import 'package:get/get.dart' as g;

import '../dio_client.dart';
import '../http_init.dart';
import '../model/response.dart';
import '../net_code.dart';

enum HttpType {
  GET,
  POST,
  PUT,
  DELETE,
  PATCH,
  HEAD,
}

extension DioClientExt on DioClient {
  Future<AppResponse<T?>> postAccept<T>(String url,
      {dynamic body,
      T? Function(dynamic data)? dataDecoder,
      Map<String, dynamic>? queryParameters,
      bool showLoading = false,
      String? loadingText,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress}) async {
    return requestAccept(url,
        method: HttpType.POST,
        body: body,
        dataDecoder: dataDecoder,
        queryParameters: queryParameters,
        showLoading: showLoading,
        loadingText: loadingText,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress);
  }

  Future<AppResponse<T?>> putAccept<T>(String url,
      {dynamic body,
      T? Function(dynamic data)? dataDecoder,
      Map<String, dynamic>? queryParameters,
      bool showLoading = false,
      String? loadingText,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress}) async {
    return requestAccept(url,
        method: HttpType.PUT,
        body: body,
        dataDecoder: dataDecoder,
        queryParameters: queryParameters,
        showLoading: showLoading,
        loadingText: loadingText,
        options: options,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress);
  }

  Future<AppResponse<T?>> getAccept<T>(String url,
      {T? Function(dynamic data)? dataDecoder,
      Map<String, dynamic>? queryParameters,
      bool showLoading = false,
      String? loadingText,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onReceiveProgress}) async {
    return requestAccept(url,
        method: HttpType.GET,
        dataDecoder: dataDecoder,
        queryParameters: queryParameters,
        showLoading: showLoading,
        loadingText: loadingText,
        options: options,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress);
  }

  Future<AppResponse<T?>> requestAccept<T>(String url,
      {dynamic body,
      required HttpType method,
      T? Function(dynamic data)? dataDecoder,
      Map<String, dynamic>? queryParameters,
      bool showLoading = false,
      String? loadingText,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress}) async {
    int errorCode = -1;
    String errorMsg = "";
    bool errorStatus = false;

    if (showLoading) {
      // g.Get.showLoading();
    }

    try {
      var response = await request(url,
          body: body,
          queryParameters: queryParameters,
          options: options ?? Options(method: method.name),
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress);
      errorCode = response.statusCode ?? errorCode;
      if (response.statusCode == HttpStatus.ok) {
        final resData = response.data as Map<String, dynamic>;
        errorCode = resData["code"] ?? -1;
        errorMsg = resData["msg"] ?? "";
        errorStatus = resData["status"] ?? false;
        switch (errorCode) {
          case NetCode.NET_CODE_OK:
            var data = resData["data"];
            return AppResponse.ok(
                null != dataDecoder && null != data ? dataDecoder(data) : data,
                code: errorCode,
                msg: errorMsg,
                status: errorStatus);
          default:
            return AppResponse.exception(NetExceptionDefault(
              errorCode,
              defaultMsg: errorMsg,
            ));
        }
      }
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) {
        // completer.completeError(e);
      } else {
        rethrow;
      }
    } catch (e, s) {
      // errorCode = NetCode.NET_CODE_FAILED;
      // errorMsg = "网络异常，请稍后再试";
      // logger.d('code: $errorCode\nmsg: $errorMsg\n$s');
      // logger.d('e: $e\ns: $s');
      // g.Get.showToast(content: S.current.badNetwork);
    } finally {
      if (showLoading) {
        // g.Get.dismiss();
      }
    }
    return AppResponse.exception(
        NetExceptionDefault(errorCode, defaultMsg: errorMsg));
  }

  Future<int?> uploadParams(
      {required FormData params,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress}) async {
    // g.Get.showLoading();
    final completer = Completer<int?>();
    try {
      final res = await dio.post(
        'iwithPad/problem/uploadFile',
        onReceiveProgress: onReceiveProgress,
        onSendProgress: onSendProgress,
        cancelToken: cancelToken,
        data: params,
        options: options ??
            Options(
              contentType: Headers.multipartFormDataContentType,
            ),
      );
      final code = res.data['code'];
      if (code == 200) {
        completer.complete(res.data['data']);
      } else {
        completer.completeError(NetExceptionDefault(
          code,
          defaultMsg: res.data['message'],
        ));
      }
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) {
        completer.completeError(e);
      } else {
        completer.completeError(NetExceptionDefault(
          e.response?.statusCode ?? -1,
        ));
      }
    } on Exception {
      // TODO
    } finally {
      // g.Get.dismiss();
    }
    return completer.future;
  }
}
